<?php defined('SYSPATH') or die('No direct script access.');

/**
 * AuthorEyes Validation Class.
 * 
 * This library extends the Kohana Validation Library, instead of using CALLBACKS in the rules.
 *
 * Why? for code reusability and tidyness!
 *
 * For example, in a normal validation rule call you would do this (with a callback):
 * |required|min_length[3]|max_length[30]|callback_username_login_check|
 *
 * But because of this library we simply call the callback as though it were a normal validation function:
 * |required|min_length[3]|max_length[30]|username_login_check|
 *
 * It is now reusable (abstract) and much easier to type as a rule!
 *
 * -----------------------------------------------------------------------------
 * Copyright (C) 2008   Patrick Clark (pubblichi.com/blog)
 * -----------------------------------------------------------------------------
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * ------------------------------------------------------------------------------
 * @package     AuthorEyes
 * @subpackage  Libraries
 * @category    Authentication
 * @author      Patrick Clark (Tap)
 * @copyright   Copyright (c) 2008, pubblichi.com
 * @license     http://www.gnu.org/licenses/lgpl.html
 * @link        http://www.pubblichi.com/authoreyes
 * @info        This is an adaptation of the FreakAuth Light authentication library,
 * @info        written specifically for Kohana.
 * @info     	FreakAuth Light written by Daniel Vecchiato (danfreak) & Christophe Gragnic (grahack)
 * @info        Visit www.4webby.com/freakauth to download FreakAuth Light for CodeIgniter
 * @version     0.1
 *
 */
class MY_AE_Validation extends Validation_Core {
	/*-------------+
	|  PROPERTIES  |
	+-------------*/
	// AuthorEyes models.
	protected $user_temp_mdl;
	protected $user_mdl;
	protected $country_mdl;
	
	// Kohana libraries.
	protected $session;
	
	/*----------+
	|  METHODS  |
	+----------*/
	/**
	 * Initialize the MY_AE_Validation class (library).
	 * 
	 * @return void
	 */
	public function __construct()
	{
		parent::__construct();
		log::add('debug', 'MY_AE_Validation class initialized.');
		
		// Instantiate models.
		$this->user_temp_mdl = new User_Temp_Model;
		$this->user_mdl = new User_Model;
		$this->country_mdl = new Country_Model;
		
		// Instantiate Kohana libraries.
		$this->session = new Session;
	}
	
	/**
	 * Function used for login validation.
	 *
	 * Validates username and password simultaneously if they passed the previous distinct
	 * individual validation.
	 *
	 * @param  string The username passed by previous validation ($username_login).
	 * @param  string The password passed by previous validation ($password_login).
	 * @return bool
	 */
	public function login_check($username_login, $password_login)
	{
		// Use the user's input name, and checks against the 'users' table.
		$query = $this->user_mdl->get_user_by_username($username_login);
		
		// Username not found.
		if ($query != NULL AND $query->count() == 0)
		{
			$username_cond = FALSE;
		}
		// Username found.
		else
		{
			$username_cond = TRUE;
		}
		
		// Ensures that our username was found and the password field contains something.
		if ($username_cond == TRUE AND isset($password_login) AND $password_login != '')
		{
			// Encrypts the password using the encryption of your choice.
			$encrypted_password = $this->authoreyes->hash_password($password_login);
			$query = $this->user_mdl->get_user_for_login($username_login, $encrypted_password);
			
			// Password incorrect.
			if ($query != NULL AND $query->count() == 0)
			{
				$pass_cond = FALSE;
			}
			// Password correct.
			else
			{
				$pass_cond = TRUE;
			}
		}
		// Username not found or password empty.
		else
		{
			$pass_cond = FALSE;
		}
		
		// Did we pass validation?
		if ($username_cond == TRUE AND $pass_cond == TRUE)
		{
			return TRUE;
		}
		// FAIL.
		else
		{
			$login_error_message = Kohana::lang('authoreyes.ae_validation.invalid_username_password_message');
			return FALSE;
		}
	}
	
	/**
	 * Rules helper function.
	 * 
	 * Password validation callback for password validation.
	 * 
	 * @param  string Value of the password for use in callback function ($value).
	 * @return bool
	 */
	private function password_check($value)
	{
		$callback = 'password_check';
		return $this->is_valid_text($callback, $value, Config::item('authoreyes.ae_user_pass_settings.user_password_min'), Config::item('authoreyes.ae_user_pass_settings.user_pass_max'));
	}
	
	/**
	 * Rule used in admin backend: it is applied only if the password field has a value,
	 * namely if the password gets changed.
	 * 
	 * @param  string Value of the password for use in callback function ($value).
	 * @return bool
	 **/
	public function password_backend_check($value)
	{
		if ($value != '' AND isset($_POST['id']))
		{
			$callback = 'password_backend_check';
			return $this->is_valid_text($callback, $value, Config::item('authoreyes.ae_user_pass_settings.user_password_min'), Config::item('authoreyes.ae_user_pass_settings.user_pass_max'));
		}
	}
	
	/**
	 * Rules helper function.
	 * 
	 * Username validation callback for validation against min-max length settings.
	 * 
	 * @param  string Value of the username for use in callback function ($value).
	 * @return bool
	 */
	private function username_check($value)
	{
		$callback = 'username_check';
		return $this->is_valid_text($callback, $value, Config::item('authoreyes.ae_user_pass_settings.username_min'), Config::item('authoreyes.ae_user_pass_settings.username_max'));
	}
	
	/**
	 * Rules helper function.
	 * 
	 * Username duplicate validation callback for validation against duplicate username in DB.
	 * 
	 * @param  string Value of the username for duplication check ($value).
	 * @return bool
	 */
	private function username_duplicate_check($value)
	{
		// Use the input username and check against the 'users' table.
		// Query in main user table (users already activated).
		$query = $this->user_mdl->get_user_by_username($value);
		
		// Query in temporary user table (users waiting for activation),
		// only if using registration with e-mail verification.
		if (!Config::item('authoreyes.ae_register_direct'))
		{
			// Building the statement
			$fields = 'id';
			$where = array('username' => $value);
			
			$query_temp = $this->user_temp_mdl->get_user_temp_where($fields, $where);
		}
		
		// Setting the right condition depending on registration type.
		if (Config::item('authoreyes.ae_register_direct'))
		{
			$condition = ($query != NULL AND $query->count() > 0) ? TRUE : FALSE;
		}
		else
		{
			$condition = ($query != NULL AND $query->count() > 0) OR ($query_temp != NULL AND $query_temp->count() > 0) ? TRUE : FALSE;
		}
		
		// Checking if condition is satisfied.
		if ($condition == TRUE)
		{
			$this->set_message('username_duplicate_check', Kohana::lang('authoreyes.ae_validation.in_use_validation_message'));
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}
	
	/**
	 * Username duplicate validation callback for validation against duplicate usernames in database.
	 * 
	 * Used in the backend.
	 * 
	 * @param  string Value of the username for duplication check ($value).
	 * @return bool  
	 */
	private function username_backend_duplicate_check($value)
	{
		// Checks if the request comes from add or edit actions.
		$fields = 'id';
		$where = (isset($_POST['id'])) ? array('id !=' => $_POST['id'], 'username' => $value) : array('username' => $value);
		$query = $this->user_mdl->get_users($fields, $limit = NULL, $where);
		
		// Query in temporary user table (users waiting for activation),
		// only if using registration with e-mail verification.
		if (!Config::item('authoreyes.ae_register_direct'))
		{
			// Building the statement
			$fields = 'id';
			$where = array('username' => $value);
			
			$query_temp = $this->user_temp_mdl->get_user_temp_where($fields, $limit = NULL, $where);
		}
		
		// Setting the right condition depending on registration type.
		if (Config::item('authoreyes.ae_register_direct'))
		{
			$condition = ($query != NULL AND $query->count() > 0) ? TRUE : FALSE;
		}
		else
		{
			$condition = ($query != NULL AND $query->count() > 0) OR ($query_temp != NULL AND $query_temp->count() > 0) ? TRUE : FALSE;
		}
		
		// Checking if condition is satisfied.
		if ($condition == TRUE)
		{
			$this->set_message('username_backend_duplicate_check', Kohana::lang('authoreyes.ae_validation.in_use_validation_message'));
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}
	
	/**
	 * Rules helper function.
	 * 
	 * Username duplicate validation callback for validation against duplicate usernames in the database.
	 * 
	 * @param  string Value of the e-mail address for duplication check ($value).
	 * @return bool
	 */
	private function email_duplicate_check($value)
	{
		// Use the user input e-mail address and check against 'users' table.
		// Query in main user table (users already activated)
		$query = $this->user_mdl->get_user_for_forgotten_password($value);
		
		if ($query != NULL AND $query->count() > 0)
		{
			$this->set_message('email_duplicate_check', Kohana::lang('authoreyes.ae_validation.user_email_duplicate'));
			return FALSE;
		}
		
		// Query in temporary user table (users waiting for activation) only if registration with e-mail verification.
		if (Config::item('authoreyes.ae_register_direct'))
		{
			// Building the statement
			$fields = 'id';
			$where = array('email' => $value);
			
			$query_temp = $this->user_temp_mdl->get_user_temp_where($fields, $where);
			
			if($query_temp != NULL and $query_temp->count() > 0)
			{
				$this->set_message('email_duplicate_check', Kohana::lang('authoreyes.ae_validation.user_temp_email_duplicate'));
				return FALSE;
			}
		}
		
		return TRUE;
	}
	
	/**
	 * Username duplicate validation callback for validation against duplicate usernames in the database.
	 * 
	 * This function uses the e-mail address to check against 'users' table.
	 * 
	 * It is used in the backend administration
	 * 
	 * @param  string Value of the e-mail address for duplication check ($value).
	 * @return bool
	 */
	private function email_backend_duplicate_check($value)
	{
		// Use the user input e-mail address and check against 'users' table.
		// Checks if the request comes from add or edit actions.
		// Query in main user table (users already activated)
		$fields = 'id';
		$where = (isset($_POST['id'])) ? array('id !=' => $_POST['id'], 'email' => $value) : array('email' => $value);
		
		$query = $this->user_mdl->get_users($fields, $limit = NULL, $where);
		
		if ($query != NULL AND $query->count() > 0)
		{
			$this->set_message('email_backend_duplicate_check', Kohana::lang('authoreyes.ae_validation.user_email_duplicate'));
			return FALSE;
		}
		
		// Query in temporary user table (users waiting for activation) only if registration with e-mail verification.
		if (Config::item('authoreyes.ae_register_direct'))
		{
			// Building the statement
			$fields = 'id';
			$where = array('email' => $value);
			
			$query_temp = $this->user_temp_mdl->get_user_temp_where($fields, $where);
			
			if($query_temp != NULL and $query_temp->count() > 0)
			{
				$this->set_message('email_backend_duplicate_check', Kohana::lang('authoreyes.ae_validation.user_temp_email_duplicate'));
				return FALSE;
			}
		}
		
		return TRUE;
	}
	
	/**
	 * Checks if the e-mail is already in use, namely if an user in the database has the same e-mail.
	 * 
	 * @param  string Value of the e-mail address for duplication check ($value).
	 * @return bool
	 */
	public function email_exists_check($value)
	{
		// Use the input e-mail and check against 'users' table.
		// Query in main user table (users already activated).
		$query = $this->user_mdl->get_user_for_forgotten_password($value);
		
		if ($query != NULL AND $query->count() > 0)
		{
			$this->set_message('email_exists_check', Kohana::lang('authoreyes.ae_forgotten_password.forgotten_password_user_not_found_message'));
			return FALSE;
		}
		
		return TRUE;
	}
	
	/**
	 * Rules helper function.
	 * 
	 * Security code validation callback for validation.
	 * 
	 * @param  string Value of the e-mail address for duplication check ($value).
	 * @return bool
	 */
	private function security_code_check($value)
	{
		// Checks to see if any of the form pages use a captcha.
		if (Config::item('authoreyes.ae_captcha_settings.ae_use_captcha_register')
			OR Config::item('authoreyes.ae_captcha_settings.use_captcha_login') 
			OR Config::item('authoreyes.ae_captcha_settings.use_captcha_forgot_password'))
		{
			// Gets the security code stored in the session.
			$security_code = $this->session->get('authoreyes_captcha');
			
			// Erase the code from the session to prevent it from being used a second time.
			$this->session->del('authoreyes_captcha');
			
			// Checks to see if captcha is using case sensitivity.
			if (!Config::item('authoreyes.ae_captcha_case_sensitive'))
			{
				$control = strcmp(strtolower($value), strtolower($security_code));
			}
			else
			{
				$control = strcmp($value, $security_code);
			}
			
			// Compares the security code provided in the input field with that stored in the session.
			if ($control != 0)
			{
				$this->set_message('security_code_check', Kohana::lang('authoreyes.ae_captcha.captcha_message'));
				return FALSE;
			}
			else
			{
				return TRUE;
			}
		}
		else
		{
			return TRUE;
		}
	}
	
	/**
	 * Rules helper function.
	 * 
	 * Checks if at least 1 country has been chosen in the select country form element.
	 * 
	 * @param  int Country ID ($country_id).
	 * @return bool
	 */
	private function country_check($country_id)
	{
		if ($country_id == 0)
		{
			$this->set_message('country_check', Kohana::lang('authoreyes.ae_validation.country_validation_message'));
			return FALSE;
		}
		
		return TRUE;
	}
	
	/**
	 * Rules helper function.
	 * 
	 * Determines if an input text has valid characters and meets min/max length requirements.
	 * 
	 * @param  string Callback function ($callback).
	 * @param  string Value of whatever string (e-mail, username, etc..) we're checking ($value).
	 * @param  int    Minimum length requirement ($min).
	 * @param  int    Maximum length requirement ($max).
	 * @param  string Invalid message ($invalid_message).
	 * @param  string Pattern matching expression ($expression).
	 * @return bool
	 */
	private function is_valid_text($callback, $value, $min, $max, $invalid_message = NULL, $expression = '^([a-z0-9])([a-z0-9_\-])*$/ix')
	{
		$message = '';
		
		if (strlen($value) < $min OR strlen($value) > $max)
		{
			$message .= sprintf(Kohana::lang('authoreyes.ae_validation.length_validation_message'), $min, $max);
		}
		
		if (!preg_match($expression, $value))
		{
			$message .= Kohana::lang('authoreyes.ae_validation.allowed_characters_validation_message');
		}
		
		if ($message != '')
		{
			if (!isset($invalid_message))
			{
				$invalid_message = Kohana::lang('authoreyes.ae_validation.invalid_validation_message');
			}
			
			$this->set_message($callback, $invalid_message.$message);
			return FALSE;
		}
		
		return TRUE;
	}
}
?>